﻿//TransactionHelper.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Quik
{
	using System;
	using System.Linq;

	using Ecng.Common;
	using Ecng.Collections;

	using StockSharp.BusinessEntities;

	static class TransactionHelper
	{
		public static Transaction CreateMoveTransaction(this Order oldOrder, Order newOrder)
		{
			if (oldOrder == null)
				throw new ArgumentNullException("oldOrder");

			if (newOrder == null)
				throw new ArgumentNullException("newOrder");

			if (oldOrder.Type == OrderTypes.ExtRepo || oldOrder.Type == OrderTypes.Repo || oldOrder.Type == OrderTypes.Rps)
			{
				throw new ArgumentOutOfRangeException("oldOrder", oldOrder.Type, "Переставляемая заявка имеет недопустимый тип.");
			}

			return
				new Transaction(TransactionTypes.ReRegister, oldOrder, newOrder)
					.SetAction(TransactionActions.MoveOrders)
					.SetSecurity(newOrder.Security)
					.SetFortsMode(newOrder.Volume == 0 ? 0 : 1)
					.SetFirstOrderId(oldOrder.Id)
					.SetFirstOrderPrice(newOrder.Price)
					.SetFirstVolume((int)newOrder.Volume);
		}

		public static Transaction CreateRegisterTransaction(this Order order)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			string orderAccount;
			if (order.Security.ExchangeBoard == ExchangeBoard.Micex)
			{
				orderAccount = (string)order.Portfolio.ExtensionInfo.TryGetValue(DdeEquityPositionColumns.Account);

				if (orderAccount.IsEmpty())
					throw new ArgumentException("Не удалось получить счёт депо для кода клиента {0}.".Put(order.Portfolio.Name));
			}
			else
			{
				orderAccount = order.Portfolio.Name;
			}

			var transaction = new Transaction(TransactionTypes.Register, order);

			transaction
				.SetAccount(orderAccount)
				.SetSecurity(order.Security)
				.SetVolume((int)order.Volume);

			//20-ти символьное составное поле, может содержать код клиента и текстовый комментарий с тем же разделителем, что и при вводе заявки вручную.
			//для ртс код клиента не обязателен
			var clientCode = order.Security.ExchangeBoard == ExchangeBoard.Micex ? order.Portfolio.Name : "";
			if (!order.Comment.IsEmpty())
			{
				//http://www.quik.ru/forum/import/24383
				var splitter = order.Portfolio.Name.Contains("/") ? "//" : "/";
				clientCode = clientCode.IsEmpty() ? order.Comment : "{0}{1}{2}".Put(clientCode, splitter, order.Comment);

				if (clientCode.Length > 20)
					clientCode = clientCode.Remove(20);
			}

			if (!clientCode.IsEmpty())
				transaction.SetClientCode(clientCode);

			if (order.ExpiryDate != null)
				transaction.SetExpiryDate(order.ExpiryDate.Value);

			switch (order.Type)
			{
				case OrderTypes.Market:
				case OrderTypes.Limit:
					transaction
						.SetOperation(order.Direction)
						.SetType(order.Type)
						.SetAction(TransactionActions.NewOrder)
						.SetPrice(order.Price);

					if (!(order.Security.Type == SecurityTypes.Future && order.ExecutionCondition == OrderExecutionConditions.PutInQueue))
						transaction.SetExecutionCondition(order.ExecutionCondition);

					break;
				case OrderTypes.Conditional:
				{
					var condition = (QuikOrderCondition)order.Condition;

					if (condition.ConditionOrder != null)
						order.Direction = condition.ConditionOrder.Direction == OrderDirections.Buy ? OrderDirections.Sell : OrderDirections.Buy;

					transaction
						.SetOperation(order.Direction)
						.SetAction(TransactionActions.NewStopOrder)
						.SetStopPrice(condition.StopPrice ?? 0);

					if (order.Price != 0)
						transaction.SetPrice(order.Price);

					string stopOrderKind;
					string stopPriceCondition;

					switch (condition.Type)
					{
						case QuikOrderConditionTypes.LinkedOrder:
							stopOrderKind = TransactionStopOrderKinds.WithLinkedLimitOrder;
							transaction
								.SetLinkedOrderPrice(condition.LinkedOrderPrice ?? 0)
								.SetLinkedOrderCancel(condition.LinkedOrderCancel);

							stopPriceCondition = order.Direction == OrderDirections.Buy ? ">=" : "<=";
							break;
						case QuikOrderConditionTypes.OtherSecurity:
							var otherSec = condition.OtherSecurity;

							if (otherSec == null)
								throw new ArgumentException();

							stopOrderKind = TransactionStopOrderKinds.ConditionPriceByOtherSecurity;
							transaction.SetOtherSecurity(otherSec);

							stopPriceCondition = condition.StopPriceCondition == QuikStopPriceConditions.MoreOrEqual ? ">=" : "<=";
							break;
						case QuikOrderConditionTypes.StopLimit:
							stopPriceCondition = null;
							stopOrderKind = null;
							break;
						case QuikOrderConditionTypes.TakeProfit:
							stopPriceCondition = null;
							stopOrderKind = TransactionStopOrderKinds.TakeProfit;
							break;
						case QuikOrderConditionTypes.TakeProfitStopLimit:
							stopPriceCondition = null;
							stopOrderKind = TransactionStopOrderKinds.TakeProfitAndStopLimit;

							if (condition.ActiveTime != null)
							{
								transaction
									.SetIsActiveInTime(true)
									.SetActiveFrom(condition.ActiveTime.Min)
									.SetActiveTo(condition.ActiveTime.Max);
							}
							else
								transaction.SetIsActiveInTime(false);

							if (condition.IsMarketTakeProfit != null)
								transaction.SetMarketTakeProfit((bool)condition.IsMarketTakeProfit);

							if (condition.IsMarketStopLimit != null)
								transaction.SetMarketStopLimit((bool)condition.IsMarketStopLimit);

							if (condition.StopLimitPrice != null)
								transaction.SetStopLimitPrice((decimal)condition.StopLimitPrice);

							break;
						default:
							throw new ArgumentOutOfRangeException();
					}

					if (condition.ConditionOrder != null)
					{
						if (stopOrderKind.IsEmpty())
							stopOrderKind = TransactionStopOrderKinds.SimpleStopLimit;

						stopOrderKind = TransactionStopOrderKinds.ActivatedByOrder + stopOrderKind;

						transaction
							.SetConditionOrderId(condition.ConditionOrder.Id)
							.SetConditionOrderUseMatchedBalance(condition.ConditionOrderUseMatchedBalance ?? false)
							.SetConditionOrderPartiallyMatched(condition.ConditionOrderPartiallyMatched ?? false);
					}

					if (condition.Type == QuikOrderConditionTypes.TakeProfit || condition.Type == QuikOrderConditionTypes.TakeProfitStopLimit)
					{
						if (condition.Offset != null)
							transaction.SetOffset(condition.Offset);

						if (condition.Spread != null)
							transaction.SetSpread(condition.Spread);
					}

					if (!stopOrderKind.IsEmpty())
						transaction.SetStopOrderKind(stopOrderKind);

					if (!stopPriceCondition.IsEmpty())
						transaction.SetStopPriceCondition(stopPriceCondition);

					break;
				}
				case OrderTypes.Repo:
				{
					var orderInfo = order.RepoInfo;

					transaction
						.SetAction(TransactionActions.NewRepoNegDeal)
						.SetOperation(order.Direction)
						.SetPrice(order.Price);

					if (orderInfo.BlockSecurities != null)
						transaction.SetBlockSecurities((bool)orderInfo.BlockSecurities);

					if (orderInfo.Partner != null)
						transaction.SetPartner(orderInfo.Partner);

					if (orderInfo.MatchRef != null)
						transaction.SetMatchRef(orderInfo.MatchRef);

					if (orderInfo.RefundRate != null)
						transaction.SetRefundRate((int)orderInfo.RefundRate);

					if (orderInfo.Rate != null)
						transaction.SetRepoRate((int)orderInfo.Rate);

					if (orderInfo.Value != null)
						transaction.SetRepoValue((decimal)orderInfo.Value);

					if (orderInfo.Term != null)
						transaction.SetRepoTerm((int)orderInfo.Term);

					if (orderInfo.SecondPrice != null)
						transaction.SetSecondPrice((decimal)orderInfo.SecondPrice);

					if (orderInfo.SettleCode != null)
						transaction.SetSettleCode(orderInfo.SettleCode);

					if (orderInfo.SettleDate != null)
						transaction.SetSettleDate((DateTime)orderInfo.SettleDate);

					if (orderInfo.LowerDiscount != null)
						transaction.SetLowerDiscount((int)orderInfo.LowerDiscount);

					if (orderInfo.StartDiscount != null)
						transaction.SetStartDiscount((int)orderInfo.StartDiscount);

					if (orderInfo.UpperDiscount != null)
						transaction.SetUpperDiscount((int)orderInfo.UpperDiscount);

					break;
				}
				case OrderTypes.Rps:
				{
					var orderInfo = order.RpsInfo;

					transaction
						.SetAction(TransactionActions.NewNegDeal)
						.SetOperation(order.Direction)
						.SetPrice(order.Price);

					if (orderInfo.Partner != null)
						transaction.SetPartner(orderInfo.Partner);

					if (orderInfo.MatchRef != null)
						transaction.SetMatchRef(orderInfo.MatchRef);

					if (orderInfo.SettleCode != null)
						transaction.SetSettleCode(orderInfo.SettleCode);

					if (orderInfo.SettleDate != null)
						transaction.SetSettleDate((DateTime)orderInfo.SettleDate);

					if (orderInfo.ForAccount != null)
						transaction.SetForAccount(orderInfo.ForAccount);

					transaction.SetCurrencyCode(orderInfo.CurrencyType.ToCurrencyName());

					break;
				}
				case OrderTypes.ExtRepo:
					throw new NotImplementedException("Регистрация заявки РЕПО-М не реализована.");
			}

			return transaction;
		}

		public static Transaction CreateCancelTransaction(this Order order)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			var transaction = new Transaction(TransactionTypes.Cancel, order);

			transaction
				.SetSecurity(order.Security);

			string action;
			Func<long, Transaction> idSetterFunc = transaction.SetOrderId;

			switch (order.Type)
			{
				case OrderTypes.Limit:
				case OrderTypes.Market:
					action = TransactionActions.KillOrder;
					break;
				case OrderTypes.Conditional:
					action = TransactionActions.KillStopOrder;
					idSetterFunc = transaction.SetStopOrderId;
					break;
				case OrderTypes.Repo:
				case OrderTypes.ExtRepo:
				case OrderTypes.Rps:
					action = TransactionActions.KillNegDeal;
					break;
				default:
					throw new ArgumentOutOfRangeException("order", order.Type, "Неизвестный тип заявки.");
			}

			idSetterFunc(order.Id).SetAction(action);

			return transaction;
		}

		public static long TryGetTransactionId(this Transaction transaction)
		{
			if (transaction.TransactionType != TransactionTypes.Register && transaction.TransactionType != TransactionTypes.ReRegister)
				return 0;
		
			var order = transaction.Orders.LastOrDefault();

			return order != null ? order.TransactionId : 0;
		}

		//public static TransactionBuilder CreateCancelOrdersTransaction(long transactionId, bool isStopOrder, string account, OrderDirections? direction, string classCode, Security security)
		//{
		//    var builder = new TransactionBuilder(TransactionTypes.CancelGroup);

		//    transactionId.CheckTransactionId();

		//    builder.SetTransactionId(transactionId);
		//    builder.SetAction(isStopOrder ? TansactionActions.KillAllStopOrders : TansactionActions.KillAllOrders);

		//    if (!account.IsEmpty())
		//        builder.SetAccount(account);

		//    if (direction != null)
		//        builder.SetOperation((OrderDirections)direction);

		//    if (classCode != null)
		//        builder.SetClassCode(classCode);

		//    if (security != null)
		//        builder.SetSecurityCode(security);

		//    return builder;
		//}

		public static bool IfFOKCancelMessage(string message)
		{
			return message.ContainsIgnoreCase("Неполное сведение FOK заявки");
		}
	}
}